#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <functional>

#include <cassert>
#include "Err.hpp"
#include "Log.hpp"

#include "Sock.hpp"
#include "Epoller.hpp"
#include "Protocol.hpp"
#include "Util.hpp"

namespace YZCTcpServer
{
    struct Connection;
    class TcpServer;

    using func_t = std::function<void(Connection *)>;
    using handlet_t = std::function<void(const std::string &)>;
    static const uint16_t defaultport = 12345;
    static const int num = 64;

    // 将一个套接字封装为一个Connection，方便处理IO数据
    struct Connection
    {
        Connection(int sockfd = -1, TcpServer *tsvrp = nullptr)
            : _sockfd(sockfd), _tsvrp(tsvrp), _lasttime(time(nullptr)) {}

        // 注册方法
        void Register(func_t r = nullptr, func_t s = nullptr, func_t e = nullptr)
        {
            _recver = r;
            _sender = s;
            _execeptr = e;
        }

        int _sockfd;            // 这个套接字的文件描述符
        std::string _inbuffer;  // 这个套接字的输入缓冲区
        std::string _outbuffer; // 这个套接字的输出内容

        func_t _recver;   // 从sock读取数据
        func_t _sender;   // 向sock写入数据
        func_t _execeptr; // 处理sock 中的IO异常事件

        TcpServer *_tsvrp; // 指向服务器的回指指针,可以被省略，但是有它的用处

        uint32_t _lasttime; // 这个连接上一次访问服务器的时间
    };

    class TcpServer
    {
    public:
        TcpServer(func_t func, uint16_t port = defaultport)
            : _port(port), _num(0), _service(func) {}
        ~TcpServer()
        {
            // epoll和listenfd在自己的析构中关闭
            if (nullptr != _revs)
                delete[] _revs;
        }

        void InitServer()
        {
            // 1.创建套接字，绑定端口，设置监听
            _sock.creatSock();
            _sock.Bind(_port);
            _sock.Listen();

            // 2.构建epoll模型
            _epoller.Create();

            // 3.将唯一的监听套接字添加到epoll模型中，ET模型需要将fd设置为非阻塞
            // 4.将listensock看作为一个Connection套接字
            AddConnection(_sock.Fd(), EPOLLIN | EPOLLET,
                          std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
            _revs = new struct epoll_event[num];
            _num = num;
        }

        // 服务器事件派发器
        void Dispatcher()
        {
            int timeout = 1000;
            while (true)
            {
                Loop(timeout);
                // LogMessage(NORMAL, "timeout ... ");

                // 如果连接中有除了监听套接字以外的连接，就需要遍历所有的连接，计算每一个连接已经有多少时间没有动了！
                if (_connections.size() > 1)
                {
                    // 1.将所有超时fd添加到closefds中
                    std::vector<int> closefds;
                    for (const auto &kv : _connections)
                    {
                        int currenttime = time(nullptr);
                        if (kv.first != _sock.Fd() && (currenttime - kv.second->_lasttime) > 60)
                        {
                            // 这个连接时间超过1分钟没有动作，将其关闭
                            LogMessage(DEBUG, "sock:%d long time no action close it", kv.first);
                            closefds.push_back(kv.first);
                        }
                    }

                    // 2.统一处理所有需要关闭的fd
                    for (const auto &fd : closefds)
                    {
                        if (_connections.count(fd))
                            Execpter(_connections[fd]);
                    }
                }
            }
        }

        // 打开一个sock对读事件，写事件的关系情况
        void EnableReadWrite(Connection *conn, bool readeable, bool writeable)
        {
            uint32_t event = (readeable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET;
            _epoller.Control(conn->_sockfd, event, EPOLL_CTL_MOD);
        }

    private:
        void Loop(int timeout)
        {
            int n = _epoller.Wait(_revs, _num, timeout);
            for (int i = 0; i < n; i++)
            {
                // 获取就绪文件的fd和就绪事件类型events
                int sock = _revs[i].data.fd;
                uint32_t events = _revs[i].events;

                // 派发事件

                // 1.将所有的异常问题转化为读写问题
                if (events & EPOLLERR) // 文件描述符发生错误
                    events |= (EPOLLIN | EPOLLOUT);
                if (events & EPOLLHUP) // 文件描述符被挂断
                    events |= (EPOLLIN | EPOLLOUT);
                // listen监听也是一种读事件，通过con对象的回调指针就能达到多态的效果
                if (IsConnectionExists(sock))
                {
                    if ((events & EPOLLIN) && _connections[sock]->_recver)
                        _connections[sock]->_recver(_connections[sock]);
                    else if ((events & EPOLLOUT) && _connections[sock]->_sender)
                        _connections[sock]->_sender(_connections[sock]);
                    else
                        ;
                }
            }
        }

        // 将文件fd和对应的事件的读写异常方法放入到一个connection对象中管理并且初始化
        void AddConnection(int sockfd, uint32_t events, func_t recver, func_t sender, func_t excepter)
        {
            // 0.将套接字设置为非阻塞
            Utile::SetNonBlock(sockfd); // 设置非阻塞

            // 1.创建sock的连接Connection,并完成初始化
            Connection *conn = new Connection(sockfd, this);
            // 1.1 注册对应的回调方法
            conn->Register(recver, sender, excepter);

            // 2. 将sock和它要关心的事件events事件"写透式"注册到epoll模型中，让epoll帮助我们关心
            bool flag = _epoller.AddEvent(sockfd, events);
            assert(flag);
            (void)flag;

            // 3. 将kv添加到connection对象中
            _connections.insert(std::make_pair(sockfd, conn));

            LogMessage(DEBUG, "add new sock:%d in epoll and unordered_map", sockfd);
        }

        // 监听套接字获取新连接，注意这里是et模型，需要处理同时到来的所有连接
        void Accepter(Connection *conn)
        {
            LogMessage(DEBUG, "Accepter In");
            // 死循环读取所有的连接
            for (;;)
            {
                // 1.创建套接字
                std::string clientip;
                uint16_t clientport = 0;
                int err = 0;
                int sockfd = _sock.Accept(&clientip, &clientport, &err);
                if (sockfd > 0)
                {
                    // 2.将套接字传入epoll模型，让其帮助我们关心事件是否就绪。还要将其添加到connection中
                    // 这里直接通过AddConnection传入即可
                    AddConnection(
                        sockfd, EPOLLIN | EPOLLET,
                        std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                        std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                        std::bind(&TcpServer::Execpter, this, std::placeholders::_1));

                    LogMessage(DEBUG, "Get a new link, info [%s:%d]", clientip.data(), clientport);
                }
                else
                {

                    if (err == EAGAIN || err == EWOULDBLOCK) // 错误码为EAGAIN，说明底层没有连接需要accept
                        break;
                    else if (err == EINTR) // 说明底层发生了中断，需要重新监听
                        continue;
                    else // 真的是监听异常了！
                        break;
                }
            }

            LogMessage(DEBUG, "Accepter Out");
        }

        // 处理接收数据
        void Recver(Connection *conn)
        {
            conn->_lasttime = time(nullptr);
            LogMessage(DEBUG, "Recver In");

            char buffer[1024];
            // 非阻塞读取数据, 发送数据后立刻返回
            while (true)
            {
                ssize_t s = recv(conn->_sockfd, buffer, sizeof(buffer) - 1, 0);
                if (s > 0)
                {
                    // 读取成功
                    buffer[s] = 0;
                    conn->_inbuffer += buffer; // 将读取的数据放入inbuffer中，之后统一处理
                }
                else if (s == 0)
                {
                    // 读取数据完毕，说明客户端退出不再发送数据了，需要从epoll中移除！
                    LogMessage(DEBUG, "Recver all data");

                    if (conn->_execeptr)
                        conn->_execeptr(conn);
                    LogMessage(DEBUG, "client quit");
                    return;
                }
                else
                {
                    // EAGAIN说明底层无数据可读，直接退出等待客户端下一次发送信息
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                        break;
                    else if (errno == EINTR)
                        continue;
                    else
                    {
                        if (conn->_execeptr)
                            conn->_execeptr(conn);
                        LogMessage(DEBUG, "Reader err");
                        return;
                    }
                }
            }

            // 一次读取全部数据之后，就去处理数据
            _service(conn);
            LogMessage(DEBUG, "Recver Out");
        }

        // 处理发送数据，如果因为没有缓冲区无法写数据之后，让epoll关心了写事件
        // 此时可能在某一个时刻没有Recv数据，却会调用Sender，此时在写完数据后，需要关闭写事件的关心！
        void Sender(Connection *conn)
        {
            conn->_lasttime = time(nullptr);
            LogMessage(DEBUG, "Sender In");
            while (true)
            {
                ssize_t s = send(conn->_sockfd, conn->_outbuffer.c_str(), conn->_outbuffer.size(), 0);
                if (s > 0)
                {
                    if (conn->_outbuffer.empty())
                    {
                        // 发送完毕
                        // EnableReadWrite(conn, false, true);
                        break;
                    }
                    else
                    {
                        // 清除缓冲区已经发送完毕的数据
                        conn->_outbuffer.erase(0, s);
                    }
                }
                else
                {
                    // 说明发送缓冲区满了,退出，等待下一次epoll通知重新发送
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                        break;
                    else if (errno == EINTR) // 中断，需要重新发送，并不是缓冲区满了
                        continue;
                    else
                    {
                        // 进入差错处理流程
                        if (conn->_execeptr)
                        {
                            conn->_execeptr(conn);
                            return;
                        }
                    }
                }
            }
            // 如果没有发送完毕，让epoll关心这个fd是否能继续发送数据
            // 通过回指指针，找到TCP中的EanbleWrite并且调用这个方法
            if (!conn->_outbuffer.empty())
                conn->_tsvrp->EnableReadWrite(conn, true, true);
            else
                conn->_tsvrp->EnableReadWrite(conn, true, false);
            LogMessage(DEBUG, "Sender Out");
        }

        // 处理异常
        void Execpter(Connection *conn)
        {
            LogMessage(DEBUG, "Execpter Begin");
            LogMessage(DEBUG, "close %d and release all mem", conn->_sockfd);

            _epoller.Control(conn->_sockfd, 0, EPOLL_CTL_DEL);
            _connections.erase(conn->_sockfd);
            close(conn->_sockfd);
            conn->_sockfd = -1;
            delete conn; // 释放不需要的推空间
        }

        bool IsConnectionExists(int sock)
        {
            return _connections.count(sock);
        }

    private:
        uint16_t _port;
        Sock _sock;
        Epoller _epoller;
        std::unordered_map<int, Connection *> _connections; // 用于管理所有的fd和它的连接
        struct epoll_event *_revs;                          // 用于获取已经就绪的事件
        int _num;                                           // revs的最大值，即就绪队列的长度
        func_t _service;
    };
}
